package de.XPortLib;

import java.util.Calendar;

import de.XPortLib.Exceptions.DataTransmissionException;
import de.XPortLib.Exceptions.TCPCommunicationException;
import de.XPortLib.Interfaces.XPortControllerIF;




public class XPortController implements XPortControllerIF {
	
	protected CommandExecutor executor;
	
	public XPortController() {
		executor = new CommandExecutor();
	}
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#getIpAddres()
	 */
	public synchronized String getIpAddress() {
		return executor.getIpAddress();
	}

	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#setIpAddres(java.lang.String)
	 */
	public synchronized void setIpAddress(String ipAddress) {
		executor.setIpAddress(ipAddress);
	}

	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#getPort()
	 */
	public synchronized int getPort() {
		return executor.getPort();
	}

	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#setPort(int)
	 */
	public synchronized void setPort(int port) {
		executor.setPort(port);
	}
			
	
	/**
	 * converts byte status to boolean array
	 * @param status
	 * @return
	 */
	private boolean[] convertStatusToArray(int status) {
		boolean[] statusArray = new boolean[CommandExecutor.NUMRELAIS];
		for (int i = 0; i < CommandExecutor.NUMRELAIS; i++) {
			if ((status & (1 << i)) > 0)
				statusArray[i] = true;
			else
				statusArray[i] = false;
		}
		return statusArray;
	}
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#readRelais()
	 */
	public synchronized boolean[] readRelais() throws DataTransmissionException, TCPCommunicationException {
		try {
			return convertStatusToArray(readRelaisConnected());
		}
		catch (DataTransmissionException ex) {
			throw ex;
		}
		catch (TCPCommunicationException ex) {
			throw ex;
		}
		finally {
			executor.disconnect();
		}
	}
	
	/**
	 * reads the current relais status
	 * @return
	 * 			boolean array with status for each relais
	 * @throws Exception
	 * @throws IOException
	 */
	private int readRelaisConnected() throws DataTransmissionException, TCPCommunicationException {
		Command readRelais = new Command(Command.CMD_READRELAIS);
		executor.executeCommandConnected(readRelais);	
		return readRelais.getAnswer()[2];
	}
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#switchRelais(int)
	 */
	public synchronized boolean[] toggleRelais(int channel) throws DataTransmissionException, TCPCommunicationException {
		
		try {
			// First ask for State of Relais
			int initalStatus = readRelaisConnected();
			int finalStatus = initalStatus;
			int[] data = { initalStatus };

			// alle anderen Relais lassen, wie sie waren
			data[0] &= (~(1 << channel));

			// den einen Kanal Invertieren und dann dazupacken
			data[0] += (~initalStatus) & (1 << channel);

			Command switchRelais = new Command(Command.CMD_SETRELAIS, data);

			//switch while initial status equals final status -> no change yet
			do {
				executor.executeCommandConnected(switchRelais);
				finalStatus = switchRelais.getAnswer()[2];
			} while (finalStatus == initalStatus);
			return convertStatusToArray(finalStatus);
		}
		catch (DataTransmissionException ex) {
			throw ex;
		}
		catch (TCPCommunicationException ex) {
			throw ex;
		}
		finally {
			executor.disconnect();
		}
	}
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#readTimer(int)
	 */
	public synchronized TimerEvent readTimer(int nr) throws DataTransmissionException, TCPCommunicationException {
		try {
			return readTimerConnected(nr);
		}
		catch (TCPCommunicationException ex) {
			throw ex;
		}
		catch (DataTransmissionException ex) {
			throw ex;
		}
		finally {
			executor.disconnect();
		}
	}
	
	
	/**
	 * reads the timerevent at given position
	 * does NOT perform connect / disconnect
	 * @param nr
	 * 			the position
	 * @return
	 * 			the TimerEvent object retrieved from XPort
	 * @throws Exception
	 * @throws IOException
	 */
	private TimerEvent readTimerConnected(int nr) throws DataTransmissionException, TCPCommunicationException {
		int[] data = { nr };
		Command readTimer = new Command(Command.CMD_GETEVENT, data, TimerEvent.SIZE);
		executor.executeCommandConnected(readTimer);
		TimerEvent timer = new TimerEvent(readTimer.getRetour(), readTimer.getAnswer()[2]);
		timer.setTimerNr(nr);
		return timer;
	}
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#readTimers()
	 */
	public synchronized TimerEvent[] readTimers() throws DataTransmissionException, TCPCommunicationException {
		try {
			int i = 1;
			//read first timer to receive total amount of timers
			TimerEvent event = readTimerConnected(0);
			int totalTimers = event.getTotalTimers();
			TimerEvent[] timers = new TimerEvent[totalTimers];
			timers[0] = event;
			
			//read remaining timers
			while(i < totalTimers) {
				timers[i]=readTimerConnected(i++);
			}
			return timers;
		}
		catch (TCPCommunicationException ex) {
			throw ex;
		}
		catch (DataTransmissionException ex) {
			throw ex;
		}
		finally {
			executor.disconnect();
		}
		
	}
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#writeTimer(int, de.XPortLib.TimerEvent)
	 */
	public synchronized int writeTimer(int nr, TimerEvent timer) throws DataTransmissionException, TCPCommunicationException {
		int[] data = { nr };
		Command writeTimer = new Command(Command.CMD_SETEVENT, data, 0, timer.getSerial());
		executor.executeCommand(writeTimer);
		return writeTimer.getAnswer()[2];
	}		
	
	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#deleteTimer(int)
	 */
	public synchronized int deleteTimer(int nr) throws DataTransmissionException, TCPCommunicationException {
		int[] data = { nr };
		Command writeTimer = new Command(Command.CMD_DELETEEVENT, data);
		executor.executeCommand(writeTimer);
		return writeTimer.getAnswer()[2];
	}	

	
	/* (non-Javadoc)
	 * @see de.XPortLib.XPortControllerIF#setDateTime(java.util.Calendar)
	 */
	public synchronized void setDateTime(Calendar cal) throws DataTransmissionException, TCPCommunicationException {
		int[] date = { cal.get(Calendar.DAY_OF_MONTH),
			(cal.get(Calendar.MONTH) + 1),
			(cal.get(Calendar.YEAR) >> 8),
			(cal.get(Calendar.YEAR) & 255),
			cal.get(Calendar.DAY_OF_WEEK) };

		int[] time = { cal.get(Calendar.HOUR_OF_DAY),
			cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND) };

		Command setDate = new Command(Command.CMD_SETDATE, date);
		Command setTime = new Command(Command.CMD_SETTIME, time);
		
		executor.executeCommand(setDate);
		executor.executeCommand(setTime);		
	}
}
